using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Text;
using System.Reflection;

namespace libJanet
{
    class Evaluator
    {
        private Evaluator(EvaluatorItem[] items)
        {
            ConstructEvaluator(items);
        }

        private Evaluator(Type returnType, string expression, string name)
        {
            EvaluatorItem[] items = { new EvaluatorItem(returnType, expression, name) };
            ConstructEvaluator(items);
        }

        private Evaluator(EvaluatorItem item)
        {
            EvaluatorItem[] items = { item };
            ConstructEvaluator(items);
        }

        private void ConstructEvaluator(EvaluatorItem[] items)
        {
            CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
            CompilerParameters comp = new CompilerParameters();
            //comp.ReferencedAssemblies.Add("system.dll");
            //comp.GenerateExecutable = false;
            //comp.GenerateInMemory = true;

            StringBuilder code = new StringBuilder();
            code.Append("using System; \n");
            code.Append("namespace libJanet { \n");
            code.Append("public class _Evaluator { \n");

            foreach (EvaluatorItem item in items)
            {
                code.AppendFormat(" public {0} {1}() ", item.ReturnType.Name, item.Name);
                code.Append(" { ");
                code.AppendFormat(" return ({0}); ", item.Expression);
                code.Append(" }\n");
            }
            code.Append("} }");

            CompilerResults cr = provider.CompileAssemblyFromSource(comp, code.ToString());
            if (cr.Errors.HasErrors)
            {
                StringBuilder error = new StringBuilder();
                error.Append("Error Compiling Expression: ");
                foreach (CompilerError err in cr.Errors)
                {
                    error.AppendFormat("{0}\n", err.ErrorText);
                }
                throw new Exception("Error Compiling Expression: " + error.ToString());
            }
            Assembly a = cr.CompiledAssembly;
            _Compiled = a.CreateInstance("libJanet._Evaluator");
        }

        #region Public Members
        private int EvaluateInt(string name)
        {
            return (int)Evaluate(name);
        }

        private string EvaluateString(string name)
        {
            return (string)Evaluate(name);
        }

        private bool EvaluateBool(string name)
        {
            return (bool)Evaluate(name);
        }

        private object Evaluate(string name)
        {
            MethodInfo mi = _Compiled.GetType().GetMethod(name);
            return mi.Invoke(_Compiled, null);
        }
        #endregion

        #region Static Members
        static internal int EvaluateToInteger(string code)
        {
            Evaluator eval = new Evaluator(typeof(int), code, staticMethodName);
            return (int)eval.Evaluate(staticMethodName);
        }

        static internal string EvaluateToString(string code)
        {
            Evaluator eval = new Evaluator(typeof(string), code, staticMethodName);
            return (string)eval.Evaluate(staticMethodName);
        }

        static internal bool EvaluateToBool(string code)
        {
            Evaluator eval = new Evaluator(typeof(bool), code, staticMethodName);
            return (bool)eval.Evaluate(staticMethodName);
        }

        static internal object EvaluateToObject(string code)
        {
            Evaluator eval = new Evaluator(typeof(object), code, staticMethodName);
            return eval.Evaluate(staticMethodName);
        }
        #endregion

        #region Private
        const string staticMethodName = "__foo";
        //Type _CompiledType = null;
        object _Compiled = null;
        #endregion
    }

    class EvaluatorItem
    {
        internal EvaluatorItem(Type returnType, string expression, string name)
        {
            ReturnType = returnType;
            Expression = expression;
            Name = name;
        }

        internal Type ReturnType;
        internal string Name;
        internal string Expression;
    }
}
